package org.modelcc.io.java.visitor;

import java.util.logging.Level;

import org.modelcc.Pattern;
import org.modelcc.io.java.JavaModelReader;
import org.modelcc.io.java.JavaLanguageVisitor;
import org.modelcc.io.java.JavaLanguageMetadata;
import org.modelcc.io.java.JavaLanguageClass;
import org.modelcc.lexer.recognizer.PatternRecognizer;
import org.modelcc.lexer.recognizer.regexp.RegExpPatternRecognizer;
import org.modelcc.lexer.recognizer.regexp.RegExps;

/**
 * Java model reader: Class visitor for @Pattern
 * 
 * @author Fernando Berzal (berzal@acm.org)
 */
public class PatternVisitor extends JavaLanguageVisitor
{	
	private JavaLanguageMetadata metadata;
	
	PatternRecognizer pattern = null;
	
	public PatternVisitor(JavaModelReader reader, JavaLanguageMetadata metadata)
	{
		super(reader);

		this.metadata = metadata;
	}
	
	@Override
	public void startVisit(Class type, JavaLanguageClass element) 
		throws Exception
	{
    	if (type.isAnnotationPresent(Pattern.class)) {
    		Pattern p = (Pattern)type.getAnnotation(Pattern.class);
    		if (p.matcher() != Pattern.class && !p.regExp().equals(Pattern.UNDEFINED)) {
    			log(Level.SEVERE, "In class \"{0}\": The @Pattern annotation cannot specify both a matcher class and a regular expression.", new Object[]{type.getCanonicalName()});
    			if (element.getValueField() != null)
    				element.setValueField(null);
    		} else {
    			Pattern an;
    			an = (Pattern) type.getAnnotation(Pattern.class);
    			if (!an.matcher().equals(Pattern.class)) {
    				if (!PatternRecognizer.class.isAssignableFrom(an.matcher()))
    					log(Level.SEVERE, "In class \"{0}\": The @Pattern class \"{1}\" does not extend PatternRecognizer.", new Object[]{type.getCanonicalName(),an.matcher().getCanonicalName()});
    				else {
    					pattern = (PatternRecognizer) an.matcher().getConstructor(String.class).newInstance(an.args());
    				}
    			} else if (!an.regExp().equals(Pattern.UNDEFINED)) {
    				if (metadata.pas.containsKey(an.regExp())) {
    					pattern = metadata.pas.get(an.regExp());
    				}  else {
    					pattern = new RegExpPatternRecognizer(an.regExp());
    					metadata.pas.put(an.regExp(),pattern);
    				}
    			} else {
    				log(Level.SEVERE, "In class \"{0}\": The @Pattern annotation has neither regular expression nor matcher class.", new Object[]{type.getCanonicalName()});
    			}
    		}
    	}

    	if (element.getValueField() != null && pattern == null) {
    		String pat = RegExps.getPattern(element.getValueField().getType());
    		if (pat != null) {
    			pattern = new RegExpPatternRecognizer(pat);
    			if ( metadata.pas.containsKey(pat)) {
    				pattern = metadata.pas.get(pat);
    		    } else {
    				pattern = new RegExpPatternRecognizer(pat);
    				metadata.pas.put(pat,pattern);
    			}
    		} else {
    			log(Level.SEVERE, "In class \"{0}\": Found non-primitive @Value field and no valid @Pattern annotation.", new Object[]{type.getCanonicalName()});
    		}
    	}

    	element.setPattern(pattern);
	}

}
